package chase.myrewards;

import java.io.ObjectInputStream;
import java.util.ArrayList;

import org.apache.http.HttpEntity;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.location.*;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import chase.myrewards.beans.LatLon;

/**
 * Location activity that has a map and shows the user nearby locations
 * at which more rewards may be earned.
 * 
 * @author Meena Abdou
 * @date 04/25/2013
 * 
 */
public class LocationActivity extends FragmentActivity {

	public final static int LOCATION_ACTIVITY = 1;
	private GoogleMap map;
	private String url;
	private String locationProvider;
	private String requestType;
	private Location myLocation;
	private ArrayList<LatLon> locations;
	private BroadcastReceiver receiver;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.location);

		receiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				finish();
			}
		};

		IntentFilter filter = new IntentFilter();

		filter.addAction("chase.myrewards.finish");
		registerReceiver(receiver, filter);

		/*********************  Set up map  **********************************************************/

		// Getting status of Google Play
		int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());

		// Showing status
		if(status==ConnectionResult.SUCCESS)
		{
			SupportMapFragment supportMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);

			// Getting a reference to the map
			map = supportMapFragment.getMap();
			Log.d("", "map is " + map);
		}
		else{

			int requestCode = 10;
			Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);
			dialog.show();
		}
		
		// Set the InfoWindow adapter
		map.setInfoWindowAdapter(new CustomInfoWindowAdapter());

		/***************  Set up location services *************************************************/

		// Acquire a reference to the system Location Manager
		LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		Log.d("LocationActivity", "locationManager is null: " + (locationManager == null));

		boolean enabledGPS = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
		boolean enabledWiFi = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

		// Check if enabled and if not send user to the GPS settings
		// Better solution would be to display a dialog and suggesting to go to the settings
		if (!enabledGPS && !enabledWiFi) {
			Log.d("LocationActivity", "GPS not on!");
			Toast.makeText(this, "GPS signal not found", Toast.LENGTH_LONG).show();
			Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
			startActivity(intent);
			Log.d("LocationActivity", "crash happened here");
			finish();
			return;
		}

		Log.d("LocationActivity", "Everything is enabled");

		// Set criteria for determining which provider to use
		Criteria criteria = new Criteria();
		criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
		locationProvider = locationManager.getBestProvider(criteria, false);
		//locationProvider = LocationManager.NETWORK_PROVIDER;

		// Get last known location and pan map to it
		Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
		if (lastKnownLocation != null){
			Log.d("", "lastKnownLocation is not null");
			myLocation = lastKnownLocation;
			LatLng lastLoc = new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude());
			map.moveCamera(CameraUpdateFactory.newLatLngZoom(lastLoc, 12));
		}

		// Define a listener that responds to location updates
		LocationListener locationListener = new LocationListener() {
			public void onLocationChanged(Location location) {
				// Called when a new location is found by the network location provider.

			}

			public void onStatusChanged(String provider, int status, Bundle extras) {}

			public void onProviderEnabled(String provider) {}

			public void onProviderDisabled(String provider) {}
		};

		// Register the listener with the Location Manager to receive location updates
		locationManager.requestLocationUpdates(locationProvider, 600000, 0.5f, locationListener);

		// Get my location
		Log.d("", "Setting myLocation");
		map.setMyLocationEnabled(true);
		if(map.getMyLocation() != null){
			myLocation = map.getMyLocation();
		}
		Log.d("", "myLocation is " + myLocation);

		/***************  Set up buttons  *****************************************************************/

		Button earnRewardsBtn = (Button) findViewById(R.id.earnPointsButton);
		earnRewardsBtn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				requestType = "earn";
				new LocationsTask().execute(requestType);
			}
		});

		Button spendRewardsBtn = (Button) findViewById(R.id.spendPointsButton);
		spendRewardsBtn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				requestType = "spend";
				new LocationsTask().execute(requestType);
			}
		});

		// Start getting locations based on favorites/recent settings
		try {
			requestType = "default";
			new LocationsTask().execute(requestType);
		}
		catch (Exception e){
			Log.e("", "Exception", e);
		}
	}

	@Override
	protected void onStop(){
		super.onStop();
		Log.d("LocationActivity", "in onStop");
	}

	@Override
	protected void onRestart(){
		super.onRestart();
		Log.d("LocationActivity", "in onRestart");
	}

	@Override
	protected void onDestroy(){
		super.onDestroy();
		unregisterReceiver(receiver);
		receiver = null;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		savePreferences();
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu){
		getMenuInflater().inflate(R.menu.menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item){ 	

		if(item.getItemId() == R.id.exit){
			String url = "http://my-rewards.appspot.com/logout";
			//String url = "http://192.168.1.4:8888/logout";

			// Call logout in MyRewardsClient to delete data and clear caches
			MyRewardsClient.getInstance().setUrl(url);
			MyRewardsClient.getInstance().Logout();
			MyRewardsClient.getInstance().setLoggedIn(false);

			// Kill other activities
			Intent finish = new Intent();
			finish.setAction("chase.myrewards.finish");
			sendBroadcast(finish);

			return true;
		}

		else if(item.getItemId() == R.id.about){
			AboutDialog about = new AboutDialog(this);
			about.setTitle("About MyRewards");
			about.show();
			return true;
		}

		else if(item.getItemId() == R.id.help){
			Intent intent = new Intent(getApplicationContext(), HelpActivity.class);
			startActivity(intent);
			return true;
		}

		else if(item.getItemId() == R.id.preferences){
			Intent intent = new Intent(LocationActivity.this, PrefsActivity.class);
			startActivityForResult(intent, PrefsActivity.PREFS_ACTIVITY);
			return true;
		}

		else{
			return super.onOptionsItemSelected(item);
		}
	}

	public void savePreferences(){
		SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);

		String password = sharedPref.getString(PrefsActivity.PASSWORD, ""); // default no password
		boolean showLocations = sharedPref.getBoolean(PrefsActivity.SHOW_LOCATIONS, true);  // default true
		boolean favorites = sharedPref.getBoolean(PrefsActivity.FAVORITES, true);  // default true
		boolean recent = sharedPref.getBoolean(PrefsActivity.RECENT, true);  // default true

		Log.d("LocationActivity", "New password is " + password);
		Log.d("LocationActivity", "New show_locations is " + showLocations);
		Log.d("LocationActivity", "New favorites is " + favorites);
		Log.d("LocationActivity", "New recent is " + recent);

		MyRewardsClient.getInstance().getCustomer().setPassword(password);
		MyRewardsClient.getInstance().getCustomer().setShowLocations(showLocations);
		MyRewardsClient.getInstance().getCustomer().setFavorites(favorites);
		MyRewardsClient.getInstance().getCustomer().setRecent(recent);
	}

	private void getLocations(String type) throws Exception{
		Log.d("LocationActivity", "in getLocations()");
		try {
			url = "http://my-rewards.appspot.com/location";
			//url = "http://192.168.1.4:8888/location";

			// Set parameters for my location
			MyRewardsClient.getInstance().setUrl(url);
			MyRewardsClient.getInstance().AddParam("myLat", String.valueOf(myLocation.getLatitude()));
			MyRewardsClient.getInstance().AddParam("myLon", String.valueOf(myLocation.getLongitude()));
			MyRewardsClient.getInstance().AddParam("requestType", type);

			// Send request to server
			MyRewardsClient.getInstance().Execute(RequestMethod.GET, false);
			HttpEntity entity = MyRewardsClient.getInstance().getResponseEntity();

			Log.d("", "getting response back from server");
			Log.d("", "response is " + entity);

			// The response will be a list of LatLons that should be shown on the map
			ObjectInputStream in = new ObjectInputStream(entity.getContent());
			locations = (ArrayList<LatLon>) in.readObject();
			in.close();

		}
		catch(Exception e){
			Log.e("LocationActivity", "", e);
		}
	}

	/**
	 * Display markers for all locations in {@link #locations}. 
	 */
	private void showLocations(){
		Log.d("LocationActivity", "in showLocations()");
		
		BitmapDescriptor icon = BitmapDescriptorFactory.defaultMarker();
		
		if (requestType.equalsIgnoreCase("default")){
			icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE);
		}
		else if(requestType.equalsIgnoreCase("earn")){
			icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN);
		}
		else if(requestType.equalsIgnoreCase("spend")){
			icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW);
		}
		
		// For every location in locations, show it on the map
		for(LatLon loc : locations){
			MarkerOptions mo = new MarkerOptions();
			mo.position(new LatLng(Double.parseDouble(loc.getLatitude()), Double.parseDouble(loc.getLongitude())));
			mo.title(loc.getName());
			mo.draggable(false);
			mo.icon(icon);
			
			// Build one string to contain address and program/promo descriptions for the snippet
			StringBuilder sbuild = new StringBuilder();
			sbuild.append(loc.getAddress() + "\n");
			
			for(int i = 0; i < loc.getProgramDes().size(); i++){
				sbuild.append(loc.getProgramDes().get(i) + "\n");
			}
			
			for(int i = 0; i < loc.getPromoDes().size(); i++){
				sbuild.append(loc.getPromoDes().get(i) + "\n");
			}
			
			mo.snippet(sbuild.toString());
			
			map.addMarker(mo);
		}
	}

	/**
	 * Every time the activity is created, this task is executed.  It merely calls
	 * {@link chase.myrewards.LocationActivity#getLocations(String)} then calls
	 * {@link chase.myrewards.LocationActivity#showLocations()}.
	 * 
	 * @author Meena Abdou
	 */
	private class LocationsTask extends AsyncTask<String, Void, Integer> {
		ProgressDialog progressDialog;

		@Override
		protected void onPreExecute(){
			progressDialog = ProgressDialog.show(LocationActivity.this, "", "Getting locations", true);
		}

		@Override
		protected Integer doInBackground(String... type) {
			try {
				getLocations(type[0]);
			} 
			catch (Exception e) {
				Log.e("", "in doInBackground ", e);
			}
			return 0;
		}

		@Override
		protected void onPostExecute(Integer v) {
			progressDialog.dismiss();
			
			if(locations == null){
				Log.d("LocationActivity", "locations is null");
				AlertDialog.Builder b = new AlertDialog.Builder(LocationActivity.this);
				b.setTitle("");
				b.setMessage("Server Error!");
				b.create().show();
			}
			
			else if (locations.isEmpty()){
				Log.d("LocationActivity", "locations is empty");
				AlertDialog.Builder b = new AlertDialog.Builder(LocationActivity.this);
				b.setTitle("");
				b.setMessage("No nearby locations");
				b.create().show();
			}
			
			else {
				showLocations();
			}
		}
	}
	
	/**
	 * Adapter for info windows, which appear when you click on markers.
	 * This class is required by Maps API v2 in order to customize the content of info windows.
	 * Each info window has the name of the location in bold, the address, and each program or
	 * promotion associated with that location.
	 * 
	 * @author Meena Abdou
	 *
	 */
	private class CustomInfoWindowAdapter implements InfoWindowAdapter {

		private final View mWindow;

		public CustomInfoWindowAdapter() {
			mWindow = getLayoutInflater().inflate(R.layout.info_window, null);
		}

		@Override
		public View getInfoWindow(Marker marker) {			
			return null;
		}

		@Override
		public View getInfoContents(Marker marker) {
			render(marker, mWindow);
			return mWindow;
		}

        private void render(Marker marker, View view) {
            String title = marker.getTitle();
            TextView titleUi = ((TextView) view.findViewById(R.id.title));
            if (title != null) {                
                titleUi.setText(title);
            }
            else {
                titleUi.setText("");
            }

            String snippet = marker.getSnippet();
            TextView snippetUi = ((TextView) view.findViewById(R.id.snippet));
            if (snippet != null) {
                snippetUi.setText(snippet);                
            } 
            else {
                snippetUi.setText("");
            }
        }
    }
}
